﻿using System;
using LoA.Shared.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace LoA.InputEngine {
	public partial class InputManager {
		/// <summary>
		/// Gets the latest input and uses it to update the input history buffer.
		/// </summary>
		public void Update(GameTime gameTime) {
			// Get latest input state.
			PreviousGamePadState = GamePadState;
			PreviousKeyboardState = KeyboardState;
			Buttons lastDirectionButtons = DirectionButtons;
			GamePadState = GamePad.GetState(PlayerIndex);
#if WINDOWS
			if (PlayerIndex == PlayerIndex.One) {
				KeyboardState = Keyboard.GetState(PlayerIndex);
			}
#endif

			// Expire old input.
			TimeSpan time = gameTime.TotalGameTime;
			TimeSpan timeSinceLast = time - LastInputTime;
			if (timeSinceLast > BufferTimeOut) {
				buffer.Clear( );
			}

			// Get all of the non-direction buttons pressed.
			Buttons buttons = 0;
			foreach (var buttonAndKey in NonDirectionButtons) {
				Buttons button = buttonAndKey.Key;
				Keys key = buttonAndKey.Value;

				// Check the game pad and keyboard for presses.
				if ((PreviousGamePadState.IsButtonUp(button) &&
					 GamePadState.IsButtonDown(button)) ||
					(PreviousKeyboardState.IsKeyUp(key) &&
					 KeyboardState.IsKeyDown(key))) {
					// Use a bitwise-or to accumulate button presses.
					buttons |= button;
				}
			}

			// It is very hard to press two buttons on exactly the same frame.
			// If they are close enough, consider them pressed at the same time.
			bool mergeInput = (buffer.Count > 0 && timeSinceLast < MergeInputTime);

			// If there is a new direction,
			DirectionButtons = InputDirection.FromInput(GamePadState, KeyboardState);
			if (lastDirectionButtons != DirectionButtons) {
				// combine the direction with the buttons.
				buttons |= DirectionButtons;

				// Don't merge two different directions. This avoids having impossible
				// directions such as Left+Up+Right. This also has the side effect that
				// the direction needs to be pressed at the same time or slightly before
				// the buttons for merging to work.
				mergeInput = false;
			}

			// If there was any new input on this update, add it to the buffer.
			if (buttons != 0) {
				if (mergeInput) {
					// Use a bitwise-or to merge with the previous input.
					// LastInputTime isn't updated to prevent extending the merge window.
					buffer[buffer.Count - 1] = buffer[buffer.Count - 1] | buttons;
				} else {
					// Append this input to the buffer, expiring old input if necessary.
					if (buffer.Count == buffer.Capacity) {
						buffer.RemoveAt(0);
					}
					buffer.Add(buttons);

					// Record this the time of this input to begin the merge window.
					LastInputTime = time;
				}
			}
		}
	}
}
